﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using VMS.FTS.APPPUSH.Utils;

namespace VMS.FTS.APPPUSH.LETIDEVICES
{
    [Serializable]
    [XmlRoot("Layout")]
    public class VMSLayout
    {
        [XmlAttributeAttribute(AttributeName = "ID", DataType = "unsignedShort")]
        public UInt16 ID;

        [XmlAttributeAttribute(AttributeName = "ItemCount", DataType = "unsignedByte")]
        public Byte ItemCount;

        [XmlAttributeAttribute(AttributeName = "EventType", DataType = "unsignedByte")]
        public Byte EventType;

        [XmlElement("Popup")]
        public List<VmsPopup> Popups;

        public VMSLayout DeepClone()
        {
            using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(memoryStream, this);
                memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
                return formatter.Deserialize(memoryStream) as VMSLayout;
            }
        }

        [XmlIgnore]
        public const int PAGE_HEADER_SIZE = 4;

        public VMSLayout()
        {
            ID = 0;
            ItemCount = 0;
            EventType = 0;
        }
        public VMSLayout(byte[] btReceive)
        {
            // TODO: Complete member initialization
            ID = (ushort)BitConverter.ToInt16(btReceive, 4);
            ItemCount = btReceive[6];
            EventType = btReceive[7]; //(EVENT_TYPE)Enum.Parse(typeof(EVENT_TYPE), btReceive[7].ToString());
            int index = 8;
            if (ItemCount > 0)
                Popups = new List<VmsPopup>();

            for (int i = 0; i < ItemCount; i++)
            {
                //POPUP p = new POPUP();

                ushort pID = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                ushort X = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);


                ushort Y = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                byte Z = btReceive[index];
                index += sizeof(byte);

                ushort Width = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                ushort Height = BitConverter.ToUInt16(btReceive, index);
                index += sizeof(ushort);

                POPUP_TYPE Type = (POPUP_TYPE)btReceive[index];
                index += sizeof(byte);

                byte pItemCount = btReceive[index];
                index += sizeof(byte);

                byte ActiveItem = btReceive[index];
                index += sizeof(byte);

                // Create popup object
                VmsPopup p = null;
                switch (Type)
                {
                    case POPUP_TYPE.CLOCK:
                        p = new TimePopup(pID,Y,X,Z,Height,Width,pItemCount,ActiveItem);
                        break;
                    case POPUP_TYPE.TEMP:
                        p = new TemperaturePopup(pID, Y, X, Z, Height, Width, pItemCount, ActiveItem);
                        break;
                    case POPUP_TYPE.TEXT:
                        p = new TextPopup(pID, Y, X, Z, Height, Width,"", pItemCount, ActiveItem);
                        break;
                    case POPUP_TYPE.PLAYLIST:
                        p = new PlaylistPopup(pID, Y, X, Z, Height, Width, pItemCount, ActiveItem);
                        break;
                    case POPUP_TYPE.UNKNOW:
                    default:
                        throw new NullReferenceException();
                        break;
                }
                if (p != null)
                {
                    unsafe
                    {
                        index += 6 /*sizeof(DATETIME)*/ * 2 + 2;
                    }
                    if (p.GetType() == typeof(PlaylistPopup))
                    {
                        ((PlaylistPopup)p).Images = new List<VmsPopupImage>();

                        for (int j = 0; j < p.ItemCount; j++)
                        {
                            VmsPopupImage img = new VmsPopupImage();

                            img.PlayOrder = btReceive[index];
                            index += sizeof(byte);

                            img.TTL = BitConverter.ToUInt16(btReceive, index);
                            index += sizeof(ushort);

                            img.TTS = BitConverter.ToUInt16(btReceive, index);
                            index += sizeof(ushort);

                            img.ItemType = (PLAYLIST_ITEM_TYPE)btReceive[index];
                            index += sizeof(byte);
                            
                            img.Content = BitConverter.ToString(btReceive, index + 2);
                            ((PlaylistPopup)p).Images.Add(img);
                        }
                    }
                    else if (p.GetType() == typeof(TextPopup))
                    {
                        ushort contentLength = BitConverter.ToUInt16(btReceive, index);
                        index += sizeof(ushort);

                        ((TextPopup)p).Content = BitConverter.ToString(btReceive, index,contentLength);
                        index += contentLength;
                    }

                    Popups.Add(p);
                }
            }
        }

        public override string ToString()
        {
            StringBuilder result = new StringBuilder();

            result.AppendFormat("{0,20} {1}\n", "Page's ID:", ID);
            result.AppendFormat("{0,20} {1}\n", "Item Count:", ItemCount);
            result.AppendFormat("{0,20} {1}\n", "Event Type:", EventType);
            if (Popups != null)
            {
                foreach (VmsPopup p in Popups)
                {
                    result.Append("***********\n");
                    result.AppendFormat("-----{0,15} {1}\n", "Popup ID:", p.ID);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup X:", p.X);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Y:", p.Y);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Z:", p.Z);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Width:", p.Width);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Height:", p.Height);
                    result.AppendFormat("-----{0,15} {1}\n", "Popup Type:", p.DataType.ToString());
                }
            }
            return result.ToString();
        }
        public VmsPopup findPopup(ushort index)
        {
            if (Popups != null)
                return Popups.Find(p => p.ID == index);

            return null;
        }

        public byte[] getBytes()
        {
            int iBufferSize = PAGE_HEADER_SIZE;
            List<byte[]> datas = null;
            if (Popups != null)
            {
                datas = new List<byte[]>();
                foreach (VmsPopup p in Popups)
                {
                    byte[] tempBuf = p.getBytes();
                    datas.Add(tempBuf);
                    iBufferSize += tempBuf.Length;
                }
            }
            byte[] buffer = new byte[iBufferSize];
            using (var fileStream = new MemoryStream(buffer))
            using (var bw = new BinaryWriter(fileStream))
            {
                bw.Write(ID);
                bw.Write(ItemCount);
                bw.Write(EventType);
                if (datas != null)
                {
                    foreach (byte[] data in datas)
                    {
                        bw.Write(data);
                    }
                }
            }
            return buffer;
        }
    }

    public enum POPUP_TYPE
    {
        [XmlEnum("Unknow")]
        UNKNOW = 0x00,
        [XmlEnum("Playlist")]
        PLAYLIST,
        [XmlEnum("Clock")]
        CLOCK,
        [XmlEnum("Temp")]
        TEMP,
        [XmlEnum("Table")]
        TABLE,
        [XmlEnum("Text")]
        TEXT
    }

    [Serializable]
    [XmlRoot("Popup")]
    [XmlInclude(typeof(TextPopup))]
    [XmlInclude(typeof(TimePopup))]
    [XmlInclude(typeof(TemperaturePopup))]
    [XmlInclude(typeof(PlaylistPopup))]
    public abstract class VmsPopup
    {
        [XmlAttribute("ID",DataType="unsignedShort")]
        public UInt16 ID;
        [XmlAttribute("X", DataType = "unsignedShort")]
        public UInt16 X;
        [XmlAttribute("Y", DataType = "unsignedShort")]
        public UInt16 Y;
        [XmlAttribute("Z", DataType = "unsignedByte")]
        public Byte Z;
        [XmlAttribute("Width", DataType = "unsignedShort")]
        public UInt16 Width;
        [XmlAttribute("Height", DataType = "unsignedShort")]
        public UInt16 Height;
        [XmlIgnore]
        public POPUP_TYPE DataType;

        [XmlIgnore]
        public DateTime StartDate;
        [XmlIgnore]
        public DateTime EndDate;

        [XmlAttribute("ItemCount", DataType = "unsignedByte")]
        public Byte ItemCount;
        [XmlAttribute("ActiveItem", DataType = "unsignedByte")]
        public Byte ActiveItem;
        [XmlIgnore]
        public ushort _reversed;
        [XmlIgnore]
        public const int POPUP_HEADER_SIZE = 28;

        public abstract byte[] getBytes();

        protected VmsPopup()
        {
        }
    }

    [Serializable]
    [XmlRoot("TimePopup")]
    public class TimePopup : VmsPopup
    {
        public TimePopup() : base() { DataType = POPUP_TYPE.CLOCK; }
        private const string CLOCK_CONFIG_STRING = "0123456789:*";
        public TimePopup(ushort id, ushort top, ushort left,byte z, ushort height, ushort width, byte itemCount,byte activeItem)
            : base()
        {
            ID = id;
            X = top; Y = left; Z = z;
            Height = height;
            Width = width;
            DataType = POPUP_TYPE.CLOCK;
            ItemCount = itemCount;
            ActiveItem = activeItem;
        }

        public override byte[] getBytes()
        {
            byte[] buffer = null;
            buffer = new byte[POPUP_HEADER_SIZE];

            if (buffer == null) return null;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            byte[] bufferDT = new byte[6];

            bufferDT[0] = 0;//(byte)StartDate.Year;
            bufferDT[1] = 0;//(byte)StartDate.Month;
            bufferDT[2] = 0;//(byte)StartDate.Day;
            bufferDT[3] = 0;//(byte)StartDate.Hour;
            bufferDT[4] = 0;//(byte)StartDate.Minute;
            bufferDT[5] = 0;//(byte)StartDate.Second;
            bufferDT.CopyTo(buffer, offset);

            offset += 6;
            bufferDT[0] = 0;//(byte)EndDate.Year;
            bufferDT[1] = 0;//(byte)EndDate.Month;
            bufferDT[2] = 0;//(byte)EndDate.Day;
            bufferDT[3] = 0;//(byte)EndDate.Hour;
            bufferDT[4] = 0;//(byte)EndDate.Minute;
            bufferDT[5] = 0;//(byte)EndDate.Second;
            bufferDT.CopyTo(buffer, offset);
            return buffer;
        }
    }

    [Serializable]
    public class TemperaturePopup : VmsPopup
    {
        public int FontId { get; set; }

        public TemperaturePopup() : base() { DataType = POPUP_TYPE.TEMP; }

        public TemperaturePopup(ushort id, ushort top, ushort left, byte z, ushort height, ushort width, byte itemCount,byte activeItem)
            : base()
        {
            ID = id;
            X = top; Y = left; Z = z;
            Height = height;
            Width = width;
            DataType = POPUP_TYPE.TEMP;
            ItemCount = itemCount;
            ActiveItem = activeItem;
        }

        public override byte[] getBytes()
        {
            byte[] buffer = null;
            buffer = new byte[POPUP_HEADER_SIZE];

            if (buffer == null) return null;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            byte[] bufferDT = new byte[6];

            bufferDT[0] = 0;//(byte)StartDate.Year;
            bufferDT[1] = 0;//(byte)StartDate.Month;
            bufferDT[2] = 0;//(byte)StartDate.Day;
            bufferDT[3] = 0;//(byte)StartDate.Hour;
            bufferDT[4] = 0;//(byte)StartDate.Minute;
            bufferDT[5] = 0;//(byte)StartDate.Second;
            bufferDT.CopyTo(buffer, offset);

            offset += 6;
            bufferDT[0] = 0;//(byte)EndDate.Year;
            bufferDT[1] = 0;//(byte)EndDate.Month;
            bufferDT[2] = 0;//(byte)EndDate.Day;
            bufferDT[3] = 0;//(byte)EndDate.Hour;
            bufferDT[4] = 0;//(byte)EndDate.Minute;
            bufferDT[5] = 0;//(byte)EndDate.Second;
            bufferDT.CopyTo(buffer, offset);
            return buffer;
        }
    }

    [Serializable]
    public class TextPopup : VmsPopup
    {
        public TextPopup() : base() { DataType = POPUP_TYPE.TEXT; }

        public TextPopup(ushort id, ushort top, ushort left, byte z, ushort height, ushort width, string content, byte itemCount, byte activeItem)
            : base()
        {
            ID = id;
            X = top; Y = left; Z = z;
            Height = height;
            Width = width;
            DataType = POPUP_TYPE.TEXT;
            ItemCount = itemCount;
            ActiveItem = activeItem;
            Content = content;
        }

        [XmlText]
        public string Content;

        public override byte[] getBytes()
        {
            byte[] buffer = null;
            // Data format:
            // <Header> <length> <string data>
            // <header> 28 bytes header popup
            // <length> string content's length
            // <string data> decoded string data
            buffer = new byte[POPUP_HEADER_SIZE + Content.Length + 2];

            if (buffer == null) return null;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            byte[] bufferDT = new byte[6];

            bufferDT[0] = 0;//(byte)StartDate.Year;
            bufferDT[1] = 0;//(byte)StartDate.Month;
            bufferDT[2] = 0;//(byte)StartDate.Day;
            bufferDT[3] = 0;//(byte)StartDate.Hour;
            bufferDT[4] = 0;//(byte)StartDate.Minute;
            bufferDT[5] = 0;//(byte)StartDate.Second;
            bufferDT.CopyTo(buffer, offset);

            offset += 6;
            bufferDT[0] = 0;//(byte)EndDate.Year;
            bufferDT[1] = 0;//(byte)EndDate.Month;
            bufferDT[2] = 0;//(byte)EndDate.Day;
            bufferDT[3] = 0;//(byte)EndDate.Hour;
            bufferDT[4] = 0;//(byte)EndDate.Minute;
            bufferDT[5] = 0;//(byte)EndDate.Second;
            bufferDT.CopyTo(buffer, offset);
            
            offset += 6;
            BitConverter.GetBytes((ushort)Content.Length).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            StringUtils.ConvertText(Content).CopyTo(buffer, offset);

            return buffer;
        }
    }

    [Serializable]
    public class PlaylistPopup : VmsPopup
    {
        public PlaylistPopup() : base() { DataType = POPUP_TYPE.PLAYLIST; }

        public PlaylistPopup(ushort id, ushort top, ushort left, byte z, ushort height, ushort width,byte itemCount,byte activeItem)
            : base()
        {
            ID = id;
            X = top; Y = left; Z = z;
            Height = height;
            Width = width;
            DataType = POPUP_TYPE.PLAYLIST;
            ItemCount = itemCount;
            ActiveItem = activeItem;
        }

        [XmlElement("Image")]
        public List<VmsPopupImage> Images;

        public override byte[] getBytes()
        {
            byte[] buffer = null;
            int index = 0;
            List<byte[]> buffers = new List<byte[]>();
            int BuffersLength = 0;

            if (Images != null && Images.Count > 0 && DataType == POPUP_TYPE.PLAYLIST)
            {
                foreach (VmsPopupImage img in Images)
                {
                    if (index < Images.Count)
                    {
                        byte[] tempbuf = img.getBytes();
                        if (tempbuf != null)
                        {
                            buffers.Add(tempbuf);
                            BuffersLength += tempbuf.Length;
                            index++;
                        }
                    }
                }
            }
            buffer = new byte[POPUP_HEADER_SIZE + BuffersLength];//+ ItemCount * VmsPopupImage.IMAGE_LENGTH];

            if (buffer == null) return null; ;
            int offset = 0;
            BitConverter.GetBytes(ID).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(X).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Y).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Z).CopyTo(buffer, offset);

            offset += sizeof(byte);
            BitConverter.GetBytes(Width).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            BitConverter.GetBytes(Height).CopyTo(buffer, offset);

            offset += sizeof(ushort);
            buffer[offset] = (byte)DataType;

            offset += sizeof(byte);
            buffer[offset] = ItemCount;

            offset += 1;
            buffer[offset] = ActiveItem;

            offset += 1;
            buffer[offset] = 0x00;// Reversed 1
            buffer[offset + 1] = 0x05;// Reversed 2

            offset += 2;
            byte[] bufferDT = new byte[6];

            bufferDT[0] = 0;//(byte)StartDate.Year;
            bufferDT[1] = 0;//(byte)StartDate.Month;
            bufferDT[2] = 0;//(byte)StartDate.Day;
            bufferDT[3] = 0;//(byte)StartDate.Hour;
            bufferDT[4] = 0;//(byte)StartDate.Minute;
            bufferDT[5] = 0;//(byte)StartDate.Second;
            bufferDT.CopyTo(buffer, offset);

            offset += 6;
            bufferDT[0] = 0;//(byte)EndDate.Year;
            bufferDT[1] = 0;//(byte)EndDate.Month;
            bufferDT[2] = 0;//(byte)EndDate.Day;
            bufferDT[3] = 0;//(byte)EndDate.Hour;
            bufferDT[4] = 0;//(byte)EndDate.Minute;
            bufferDT[5] = 0;//(byte)EndDate.Second;
            bufferDT.CopyTo(buffer, offset);
            
            offset += 6;
            if (Images != null && Images.Count > 0 && DataType == POPUP_TYPE.PLAYLIST)
            {
                foreach (byte[] buf in buffers)
                {
                    buf.CopyTo(buffer, offset);
                    offset += buf.Length;
                }
            }
            return buffer;
        }
    }

    public enum EFFECT
    {
        [XmlEnum("Static")]
        [Description("#e0")]
        STATIC = 0x00,
        [XmlEnum("TopDown")]
        [Description("#e1")]
        TOPDOWN,
        [XmlEnum("BottomUp")]
        [Description("#e2")]
        BOTTOMUP,
        [XmlEnum("LeftRight")]
        [Description("#e3")]
        LEFTRIGHT,
        [XmlEnum("RightLeft")]
        [Description("#e4")]
        RIGHTLEFT,
    }

    public enum PLAYLIST_ITEM_TYPE
    {
        [XmlEnum("Unknow")]
        UNKNOW = 0x00,
        [XmlEnum("Text")]
        TEXT ,
        [XmlEnum("Image")]
        IMAGE,
        [XmlEnum("Clock")]
        CLOCK,
        [XmlEnum("Temp")]
        TEMP
    }

    [Serializable]
    public class VmsPopupImage
    {
        [XmlAttribute("PlayOrder",DataType="unsignedByte")]
        public Byte PlayOrder;
        
        // tungpt added >>
        [XmlAttribute("TTL", DataType = "unsignedShort")]
        public UInt16 TTL;// convert to int
        [XmlAttribute("TTS", DataType = "unsignedShort")]
        public UInt16 TTS;// convert to int
        [XmlAttribute("Type")]
        public PLAYLIST_ITEM_TYPE ItemType = PLAYLIST_ITEM_TYPE.UNKNOW;
        [XmlText]
        public string Content;
        // << ended
        /*
        [XmlAttribute("ImageCode",DataType="unsignedShort")]
        public UInt16 ImageCode;// convert to int
        [XmlAttribute("Delay",DataType="unsignedShort")]
        public UInt16 Delay;
        [XmlAttribute("Effect")]
        public EFFECT Effect = EFFECT.STATIC;
          
        [XmlIgnore]
        public const int IMAGE_LENGTH = 6;// update length
          */
        public VmsPopupImage()
        { }

        public VmsPopupImage(byte pOrder, ushort ttl, ushort tts, PLAYLIST_ITEM_TYPE type,string content)
        {
            PlayOrder = pOrder;
            TTL = ttl;
            TTS = tts;
            ItemType = type;
            Content = content;
        }

        public byte[] getBytes()
        {
            if (Content == null) return null;
            if (ItemType == PLAYLIST_ITEM_TYPE.IMAGE && Content.Length != 2) return null;

            byte[] buffer = new byte[8 + Content.Length];
            buffer[0] = PlayOrder;
            BitConverter.GetBytes(TTL).CopyTo(buffer, 1);
            BitConverter.GetBytes(TTS).CopyTo(buffer, 3);
            buffer[5] = (byte)ItemType;
            BitConverter.GetBytes((ushort)Content.Length).CopyTo(buffer, 6);

            if (ItemType == PLAYLIST_ITEM_TYPE.TEXT )
                StringUtils.ConvertText(Content).CopyTo(buffer, 8);
            else if (ItemType == PLAYLIST_ITEM_TYPE.IMAGE)
            {
                try
                {
                    BitConverter.GetBytes(ushort.Parse(Content)).CopyTo(buffer, 8);
                }
                catch (Exception e)
                {
                    return null;
                }
            }
            else if (ItemType == PLAYLIST_ITEM_TYPE.CLOCK ||
                ItemType == PLAYLIST_ITEM_TYPE.TEMP)
            {
                try
                {
                    BitConverter.GetBytes(byte.Parse(Content.Substring(0,1))).CopyTo(buffer, 8);
                }
                catch (Exception e)
                {
                    return null;
                }
                StringUtils.ConvertText(Content.Substring(1)).CopyTo(buffer, 9);
            }

            return buffer;
        }
    }
}
